package kgame5.kgu.move.quadtree {
import kgame5.k3d.core.math.Vector3D;
import kgame5.k3d.core.obj3d.hardvo.HardVO;

/**
 * @author kk
 * 操作QTree的一个工具，
 * 1，简单碰撞检测
 * 2，实体移动的碰撞检测，自动绕行
 * 
 * ！！注意自动绕行的逻辑是，如果之碰上了一个障碍物，且我半边身子在外侧，则绕行
 * 能绕行的安全范围是障碍物是坦克大小的一半
 * !!严重注意，自动绕行的时候，如果尝试绕行且碰到了障碍物，则要把v.x,v.z设置成0，否则服务器混乱
 * !!java端的HitTestAABB做好了,使用x2而不是/2,速度快些，节约运算
 * 
 * 1,自动绕行比较复杂，注意如果是坦克自动绕行后装上障碍物，把速度设置为0要，否则服务器会认为坦克改变了方向而绕行 ok
 */
public class QTManager {
	public static const DETOUR_SPEED:int=2;
	public static const STEPBYSTEP_TIMES:int=10;//逼近的时候小步前进的次数
	public static const APPROACH_TIMES:int=3;//逼近算法分3次
	
	//只需要知道是否碰撞上了,碰上谁无所谓
	public function isCollide(vo:HardVO,tree:QuadTree):Boolean{
		//--0:定位到最合适的节点，完全包围
		var aimNode:QuadTreeNode=tree.Find(tree.root, vo);
		//vo.dataObj=aimNode.depth;//测试用的
		
		//-碰撞检测
		return FastCD(vo,aimNode);
	}
	
	//快速碰撞检测，只需要知道自己是否和周围的东西碰撞了否,主要用来人物移动
	//aimNode是定位到的vo最合适的节点
	private function FastCD(vo:HardVO,aimNode:QuadTreeNode):Boolean{
		//T.p("FastCD");
		
		//T.p("定位当前节点",aimNode.depth);
		//--1:检查是否与当前节点中物体碰撞
		if(aimNode.fastCD(vo)){
			//T.p("当前");
			return true;
		}
		
		//--2:向上检查，看是否有跟父节点碰撞的情况.一路向上即可，不需要计算与兄弟的碰撞
		var upNode:QuadTreeNode=aimNode.parentNode;
		while(upNode!=null){
			if(upNode.fastCD(vo)){
				//T.p("父亲节点");
				return true;
			}
			upNode= upNode.parentNode;
		}
		
		
		//--3:向下检查，看是否与子有碰撞
		return _childrenCD(aimNode,vo);
	}
	
	//检查某node的子是否撞上
	private function _childrenCD(node:QuadTreeNode,vo:HardVO):Boolean{
		for each(var childNode:QuadTreeNode in node.aChildNode){
			if(childNode.fastCD(vo)){
				return true;
			}
		}
		
		for each(var childNode:QuadTreeNode in node.aChildNode){
			if( _childrenCD(childNode,vo)){
				return true;
			}
		}
		
		return false;
	}
	
	//处理有一个不在tree内的hardVO的移动,注意此时vo的vx,vz都要是设置好的
	//处理人物漫游，前提是人物有速度
	//常规的漫游，不包含自动绕行,撞上后贴近障碍物
	//返回的是是否撞上东西了
	//这个主要用来做炮弹飞行碰撞用的
	public function simpleWander(vo:HardVO,tree:QuadTree):Boolean{
		var _isCollide:Boolean=false;
		//快捷引用
		var p:Vector3D = vo.v3d_p;
		var v:Vector3D = vo.v3d_v;
		var preP:Vector3D = vo._preP;
		
		//如果静止，则不需要运算
		if(v.x==0
			&&v.z==0){
				_isCollide=false;
			return _isCollide;
		}
		
		preP.copy(p);//保持上一个位置
		
		p.add(v);//加上速度
		
		//碰撞检测
		//--0:定位到最合适的节点，完全包围
		var aimNode:QuadTreeNode=tree.Find(tree.root, vo);
		//vo.dataObj=aimNode.depth;//测试用的
		
		//-碰撞检测
		if(FastCD(vo,aimNode)){
			//T.p("-------------------检测到装上了!!------------------");
			//1,放回原来位置。
			p.copy(preP);
			
			//2,贴紧边框。使用3次2分逼近算法，
			approach(vo,0,aimNode);
			
			_isCollide=true;
		}
		
		return _isCollide;
	}
	
	//逐步逼近，让这个东西贴紧到障碍物上
	private function approach(vo:HardVO,depth:int,aimNode:QuadTreeNode){
		//T.p("----approach:"+depth, vo.v3d_v);
		
		//获得引用
		var p:Vector3D=vo.v3d_p;
		var v:Vector3D=vo.v3d_v;
		
		//-------如果是最大层次了，不再继续--------
		if(depth==APPROACH_TIMES){//到达逼近的极限了
			return;
		}
		
		//---------简单小步前进一下，撞上则立即返回---------
		//为了处理贴紧墙的问题，做一个优化，先向前一小步，看是不是撞上
		//这个因为当贴紧墙的时候，用户再按键，包成快速的有个贴紧的结果，而不必走那么多流程的东西
		var stepZ:int;
		var stepX:int;
		//////////
		if(v.x==0||v.z==0){//4方向的,精确步进
				
				if(v.x==0){
					stepZ=v.z/Math.abs(v.z);
				}
				if(v.z==0){
					stepX=v.x/Math.abs(v.x);
				}
				//T.p("step:",stepX,stepZ);
			
				p.x+=stepX;
				p.z+=stepZ;
					
				if(FastCD(vo,aimNode)){
					p.x-=stepX;
					p.z-=stepZ;
					//T.p("快速测试的时候撞上了,直接返回");
					return;
				}
				
				//快速测试没发现碰撞，放回原位置
				p.x-=stepX;
				p.z-=stepZ;
		}
		/////////
		
		//------------速度减半，测试---------------
		//速度减半，测试
		v.x=(int)(v.x*0.5);
		v.z=(int)(v.z*0.5);
		//T.p("减半后速度",v);
		//速度如果已经很小了，则不再执行逼近算法了
		if(Math.abs(v.x)<1&&Math.abs(v.z)<1){
			return;
		}
		
		//保持上一个位置
		var preP:Vector3D=p.clone();
		//尝试放到新位置
		p.add(v);
		
		//检测碰撞
		if(FastCD(vo,aimNode)){//撞上了
			//T.p("-aproachOKKKKKKKK");
			p.copy(preP);//放回原来位置
			approach(vo,depth+1,aimNode);//速度减半继续测试
		}else{//没撞上,此时位置为p+v/2,v为v/2
			//T.p("-aproach撞上！！！！！",v);
			if(v.x==0||v.z==0){//4方向的,逐步步进
				
				if(v.x==0){
					stepZ=v.z/Math.abs(v.z);
				}
				if(v.z==0){
					stepX=v.x/Math.abs(v.x);
				}
				//T.p("step:",stepX,stepZ);
				var t:int=0;//保证不会死循环
				while(true){
					//T.p("--向前一步");
					t++;
					if(t==STEPBYSTEP_TIMES){
						//T.p("靠近次数太多，结束");
						return;
					}
					p.x+=stepX;
					p.z+=stepZ;
					
					if(FastCD(vo,aimNode)){
						p.x-=stepX;
						p.z-=stepZ;
						//T.p("以单位速度逼近，撞上了");
						return;
					}
				}
				
			}else{//360度的，使用不精确的定位
				approach(vo,depth+1,aimNode);//速度减半继续测试
			}
		}
	}
	
	//带自动绕行的漫游,检查边界
	public function playerWander(vo:HardVO,tree:QuadTree){
		//T.p("playerWander");
		//快捷引用
		var p:Vector3D = vo.v3d_p;
		var v:Vector3D = vo.v3d_v;
		var preP:Vector3D = vo._preP;
		
		//如果静止，则不需要运算
		if(v.x==0
			&&v.z==0){
			return;
		}
		
		preP.copy(p);//保持上一个位置
		
		p.add(v);//加上速度
		
		//碰撞检测
		//--0:定位到最合适的节点，完全包围
		var aimNode:QuadTreeNode=tree.Find(tree.root, vo);
		//vo.dataObj=aimNode.depth;//测试用的
		
		//-碰撞检测
		BestCD(vo,aimNode);
		//vo.dataObj=_arr.length;
		//T.p(_arr.length);
		if(_arr.length>0){
			//T.p("-------------------检测到装上了!!------------------");
			//1,放回原来位置。
			p.copy(preP);
			
			//2,贴紧边框。使用3次2分逼近算法，
			approach(vo,0,aimNode);
			
			//3,自动绕行,只碰上一个障碍物
			if(_arr.length==1){
				this.autoDetour(vo,_arr[0] as HardVO,aimNode);
			}
		}
		
		//检查一下位置，如果超了边界，则贴紧边界
		this.checkBound(vo, tree);
	}
	
	//自动绕行,p是player,b是block
	//注意只有在四个方向的时候才绕行，因为360方向，他自己会有分速度去绕行的
	//!!自动绕行很简单，很短的代码就能解决问题
	private function autoDetour(p:HardVO,b:HardVO,aimNode:QuadTreeNode){
		//T.p("autoDetour");
		var pp:Vector3D = p.v3d_p;
		var pv:Vector3D = p.v3d_v;
		
		var bp:Vector3D = b.v3d_p;
		
		//T.p("playerP",pp);
		//T.p("playerV",pv);
		//T.p("bar",bp);
		//T.p("barAB",b.aabb.A,b.aabb.B);
		var bA2:int=b.aabb.A>>1;
		var bB2:int=b.aabb.B>>1;
		
		var up:int=bp.z+bB2;
		var down:int=bp.z-bB2;
		var left:int=bp.x-bA2;
		var right:int=bp.x+bA2;
		//T.p(up,down,left,right);
		
		var pA2 : int = p.aabb.A>>1;
		var pB2 : int = p.aabb.B>>1;
		var myLeft:int=pp.x-pA2;
		var myRight:int=pp.x+pA2;
		var myUp:int=pp.z+pB2;
		var myDown:int=pp.z-pB2;
		
		if(pv.x==0){//上下方向
			
			if(myLeft<left&&myRight>right){//障碍物比我小，且被我包围，我无法绕行
				return;
			}
			
			if((pv.z>0&&bp.z>pp.z)//---------------向上走且障碍物在玩家之上
			||(pv.z<0&&bp.z<pp.z)//---------------向下走且障碍物在玩家之下
			){
				
					if(myLeft<left){//可向左绕行一下
						pv.x=-DETOUR_SPEED;//设置速度
						pv.z=0;
						//尝试加到指定位置，有碰撞则返回即可
						testPlace(p,aimNode);
					}else if(myRight>right){//可向右绕行一下
						pv.x=DETOUR_SPEED;//设置速度
						pv.z=0;
						//尝试加到指定位置，有碰撞则返回即可
						testPlace(p,aimNode);
					}
				
				
			}
		}
		
		if(pv.z==0){//左右方向
		
			if(myUp>up&&myDown<down){//障碍物比我小，且被我包围，我无法绕行
				return;
			}
		
			if((pv.x>0&&bp.x>pp.x)//---------------向上走且障碍物在玩家之上
			||(pv.x<0&&bp.x<pp.x)//---------------向下走且障碍物在玩家之下
			){
				
					if(myUp>up){//可向上绕行一下
						pv.z=DETOUR_SPEED;//设置速度
						pv.x=0;
						//尝试加到指定位置，有碰撞则返回即可
						testPlace(p,aimNode);
					}else if(myDown<down){//可向右绕行一下
						pv.z=-DETOUR_SPEED;//设置速度
						pv.x=0;
						//尝试加到指定位置，有碰撞则返回即可
						testPlace(p,aimNode);
					}
				
				
			}
		}
	}
	
	//测试把东西放下
	private function testPlace(hardVO:HardVO,aimNode:QuadTreeNode){
		var p:Vector3D = hardVO.v3d_p;
		var v:Vector3D = hardVO.v3d_v;
		
		var _preP:Vector3D= hardVO._preP;
		
		_preP.copy(p);//保持原始位置
		
		p.add(v);//加到指定位置
						
		if(this.FastCD(hardVO, aimNode)){//如果撞上，
			//则放回原来位置
			p.copy(_preP);
			//速度为0，不再移动,!!严重注意这个，否则会出现客户端服务器不同步的东西，坦克绕行混乱
			v.x=0;
			v.z=0;
		}
	}
	
	//==========================BestCD================
	//为自动绕行用的，找出所有与此物碰撞的东西来
	//快速碰撞检测，只需要知道自己是否和周围的东西碰撞了否,主要用来人物移动
	//aimNode是定位到的vo最合适的节点
	private var _arr:Array=new Array();//里面存储的是hardVO
	private function BestCD(vo:HardVO,aimNode:QuadTreeNode){
		//T.p("BestCD");
		_arr.length=0;//清空一下
		//_arr=new Array();
		//T.p("定位当前节点",aimNode.depth);
		//--1:检查是否与当前节点中物体碰撞
		aimNode.bestCD(vo,_arr);
		
		//--2:向上检查，看是否有跟父节点碰撞的情况.一路向上即可，不需要计算与兄弟的碰撞
		var upNode:QuadTreeNode=aimNode.parentNode;
		while(upNode!=null){
			upNode.bestCD(vo,_arr);
			
			upNode= upNode.parentNode;
		}
		
		
		//--3:向下检查，看是否与子有碰撞
		_childrenCD_best(aimNode,vo,_arr);
		
	}
	
	//检查某node的子是否撞上
	private function _childrenCD_best(node:QuadTreeNode,vo:HardVO,_arr:Array){
		for each(var childNode:QuadTreeNode in node.aChildNode){
			if(childNode!=null){
				childNode.bestCD(vo,_arr);
			}
		}
		
		for each(var childNode:QuadTreeNode in node.aChildNode){
			if(childNode!=null){
				_childrenCD_best(childNode,vo,_arr);
			}
		}
		
	}
	
	//检查是否越界，越界则贴上墙,上下和左右方向
	private function checkBound(vo:HardVO,tree:QuadTree){
		var p:Vector3D = vo.v3d_p;
		var A2: int = vo.aabb.A>>1;
		var B2: int = vo.aabb.B>>1;
		
		var leftBound:int = tree.left+A2;
		var rightBound:int = tree.right-A2;
		
		var upBound:int=tree.up-B2;
		var downBound:int=tree.down+B2;
		
		if(p.x<leftBound){
			p.x=leftBound;
		}else if(p.x>rightBound){
			p.x=rightBound;
		}
		
		if(p.z>upBound){
			p.z=upBound;
		}else if(p.z<downBound){
			p.z=downBound;
		}
	}
}//end of class
}
